package storemanmpc

import (
	"errors"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/rlp"
	"github.com/wanchain/go-mpc/storeman/btc"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	"github.com/wanchain/go-mpc/storeman/schnorrmpc"
	"github.com/wanchain/go-mpc/storeman/schnorrmpcbn"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"github.com/wanchain/go-mpc/storeman/storemanmpc/step"
)

//send create LockAccount from leader
func reqSignMpc(mpcID uint64, peers []mpcprotocol.PeerInfo, peerCurCount uint16, curveType uint8, preSetValue ...MpcValue) (*MpcContext, error) {
	result := createMpcBaseMpcResult()
	result.InitializeValue(preSetValue...)
	result.SetMpcContextId(mpcID)

	_, grpIdString, _ := osmconf.GetGrpId(result)
	totalNum, _ := osmconf.GetOsmConf().GetTotalNum(grpIdString)

	mpc := createMpcContext(mpcID, peers, result, curveType)

	reqMpc := step.CreateRequestMpcStep(int(totalNum), peerCurCount, mpcprotocol.MpcSignLeader)
	//todo review  no need to wait ???
	reqMpc.SetWaiting(int(peerCurCount))

	mpcReady := step.CreateMpcReadyStep(int(totalNum))
	mpcReady.SetWaiting(0)

	return generateTxSignMpc(mpc, reqMpc, mpcReady, peerCurCount, curveType)
}

//get message from leader and create Context
func ackSignMpc(mpcID uint64, peers []mpcprotocol.PeerInfo, peerCurCount uint16, curveType uint8, preSetValue ...MpcValue) (*MpcContext, error) {
	result := createMpcBaseMpcResult()
	result.InitializeValue(preSetValue...)
	result.SetMpcContextId(mpcID)

	mpc := createMpcContext(mpcID, peers, result, curveType)

	_, grpIdString, _ := osmconf.GetGrpId(result)
	totalNum, _ := osmconf.GetOsmConf().GetTotalNum(grpIdString)

	ackMpc := step.CreateAckMpcStep(int(totalNum), mpcprotocol.MpcSignPeer)
	ackMpc.SetWaiting(0)

	mpcReady := step.CreateGetMpcReadyStep(int(totalNum))
	mpcReady.SetWaiting(1)

	return generateTxSignMpc(mpc, ackMpc, mpcReady, peerCurCount, curveType)
}

func generateTxSignMpc(mpc *MpcContext, firstStep MpcStepFunc, readyStep MpcStepFunc, peerCurCount uint16, curveType uint8) (*MpcContext, error) {
	log.SyslogInfo("generateTxSignMpc begin")

	result := mpc.mpcResult
	_, grpIdString, err := osmconf.GetGrpId(result)
	if err != nil {
		log.SyslogErr("generateTxSignMpc", "GetGrpId error", err.Error())
		return nil, err
	}

	totalNum, _ := osmconf.GetOsmConf().GetTotalNum(grpIdString)

	threshold, _ := osmconf.GetOsmConf().GetThresholdNum(grpIdString)
	if threshold < uint16(1) {
		log.SyslogErr("generateTxSignMpc", "threshold error", threshold)
		return nil, errors.New("invalid threshold")
	}
	degree := threshold - 1

	accTypeStr := ""

	// 2
	cmStep := step.CreateMpcPolycmStep(int(totalNum))
	cmStep.SetWaiting(int(peerCurCount + 1))

	// 3
	skShare := step.CreateMpcRSKShareStep(int(degree), int(totalNum))
	skShare.SetWaiting(int(peerCurCount + 1)) // not broadcast, only need receive peerCurCount data.

	// 4
	skJudgeStep := step.CreateMpcRSkJudgeStep(int(totalNum))
	// only handle the first Rsk challenge or (timeout no challenge)
	skJudgeStep.SetWaiting(1)
	//skJudgeStep.SetWaiting(int(peerCurCount + 1))

	// 5
	// add rrcvInter step
	rrcvInterStep := step.CreateMpcRRcvInterStep(int(totalNum))
	rrcvInterStep.SetWaiting(int(peerCurCount + 1))

	// 6
	// add rrcvInter judge step
	rrcvJudgeStep := step.CreateMpcRRcvJudgeStep(int(totalNum))
	rrcvJudgeStep.SetWaiting(int(peerCurCount + 1))

	// 7
	RStep := step.CreateMpcRStep(int(totalNum), accTypeStr)
	//since RPKShare is used to check the content of sshare, so it should collect all node's RPK.
	//otherwise, when check sshare content system can not find the RPKShare.
	rrcvJudgeStep.SetWaiting(int(peerCurCount + 1))
	//RStep.SetWaiting(int(threshold))

	// 8
	SStep := step.CreateMpcSStep(int(totalNum), []string{mpcprotocol.MpcPrivateShare}, []string{mpcprotocol.MpcS})
	//SStep.SetWaiting(int(threshold))
	SStep.SetWaiting(int(peerCurCount + 1))

	// 9
	sshareJudgeStep := step.CreateMpcSSahreJudgeStep(int(totalNum))
	// only handle the first sshare challenge or (timeout no challenge)
	//sshareJudgeStep.SetWaiting(1)
	//sshareJudgeStep.SetWaiting(int(peerCurCount + 1))
	sshareJudgeStep.SetWaiting(int(peerCurCount))

	// 10
	ackRSStep := step.CreateAckMpcRSStep(int(totalNum), accTypeStr)
	//ackRSStep.SetWaiting(int(peerCurCount + 1))
	ackRSStep.SetWaiting(int(peerCurCount))

	mpc.setMpcStep(firstStep,
		readyStep,
		cmStep,
		skShare,
		skJudgeStep,
		rrcvInterStep,
		rrcvJudgeStep,
		RStep,
		SStep,
		sshareJudgeStep,
		ackRSStep)

	for stepId, stepItem := range mpc.MpcSteps {
		//stepItem.SetWaiting(len(mpc.peers) + 1)
		stepItem.SetWaitAll(false)
		stepItem.SetStepId(stepId)

		stepItem.SetMpcContextId(mpc.ContextID)

		stepItem.SetMpcContext(mpc)
	}

	var schnorrMpcer mpcprotocol.SchnorrMPCer
	switch int(curveType) {
	case mpcprotocol.SK256Curve:
		log.SyslogInfo("Using SK256 curve")
		schnorrMpcer = schnorrmpc.NewSkSchnorrMpc()
	case mpcprotocol.BN256Curve:
		log.SyslogInfo("Using BN256 curve")
		schnorrMpcer = schnorrmpcbn.NewBnSchnorrMpc()
	default:
		log.SyslogInfo("Using SK256 curve (default...)")
		schnorrMpcer = schnorrmpc.NewSkSchnorrMpc()
	}

	mpc.SetSchnorrMPCer(schnorrMpcer)
	mpc.SetStepSchnorrMPCer()

	mpc.SetStepCurveType(uint16(curveType))

	for _, step := range mpc.MpcSteps {
		msgGens := step.GetMsgGens()
		if msgGens != nil {
			for _, msgGen := range msgGens {
				msgGen.SetSchnorrMpcer(schnorrMpcer)
			}
		}
	}
	return mpc, nil
}

/// add for BTC begin

func reqSignMpcBtc(mpcID uint64, peers []mpcprotocol.PeerInfo, peerCount uint16, curveType uint8, preSetValue ...MpcValue) (*MpcContext, error) {
	result := createMpcBaseMpcResult()
	result.InitializeValue(preSetValue...)
	result.SetMpcContextId(mpcID)

	mpc := createMpcContext(mpcID, peers, result, curveType)
	_, grpIdString, _ := osmconf.GetGrpId(result)
	totalNum, _ := osmconf.GetOsmConf().GetTotalNum(grpIdString)

	requestMpc := step.CreateRequestMpcStepBtc(int(totalNum), peerCount, mpcprotocol.MpcSignLeaderBtc)
	// Not count receive message from self.
	requestMpc.SetWaiting(int(peerCount))

	mpcReady := step.CreateMpcReadyStep(int(totalNum))
	return generateTxSignMpcBtc(mpc, requestMpc, mpcReady, peerCount, curveType)
}

func ackSignMpcBtc(mpcID uint64, peers []mpcprotocol.PeerInfo, peerCount uint16, curveType uint8, preSetValue ...MpcValue) (*MpcContext, error) {
	result := createMpcBaseMpcResult()
	result.InitializeValue(preSetValue...)
	result.SetMpcContextId(mpcID)

	mpc := createMpcContext(mpcID, peers, result, curveType)

	_, grpIdString, _ := osmconf.GetGrpId(result)
	totalNum, _ := osmconf.GetOsmConf().GetTotalNum(grpIdString)

	ackMpc := step.CreateAckMpcStep(int(totalNum), mpcprotocol.MpcSignPeerBtc)
	ackMpc.SetWaiting(0)

	// wait 0
	mpcReady := step.CreateGetMpcReadyStep(int(totalNum))
	mpcReady.SetWaiting(1)
	// wait 1
	return generateTxSignMpcBtc(mpc, ackMpc, mpcReady, peerCount, curveType)
}

func generateTxSignMpcBtc(mpc *MpcContext, firstStep MpcStepFunc, readyStep MpcStepFunc, peerCount uint16, curveType uint8) (*MpcContext, error) {
	log.SyslogInfo("generateTxSignMpc begin")

	signNum, err := getSignNumFromTxInfo(mpc)
	if err != nil {
		return nil, err
	}

	// 2
	// get degree from config.
	_, grpId, err := osmconf.GetGrpId(mpc.mpcResult)
	if err != nil {
		return nil, err
	}
	degree, _ := osmconf.GetOsmConf().GetThresholdNumByCurve(grpId, curveType)
	totalNum, _ := osmconf.GetOsmConf().GetTotalNum(grpId)

	log.SyslogInfo("generateTxSignMpcBtc", "degree", degree)
	JRJZ := step.CreateTXSignJR_JZ_Step(int(degree), int(totalNum), signNum)
	JRJZ.SetWaiting(int(peerCount + 1))

	// 3
	pointStepPreValueKeys := mpcprotocol.GetPreSetKeyArr(mpcprotocol.MpcSignA0, signNum)
	pointStepResultKeys := mpcprotocol.GetPreSetKeyArr(mpcprotocol.MpcSignAPoint, signNum)
	AGPoint := step.CreateMpcPoint_Step(int(totalNum), pointStepPreValueKeys, pointStepResultKeys)
	AGPoint.SetWaiting(int(peerCount + 1))

	// 4
	peerIntersaction := step.CreateMpcPeerIntersactionStep(int(totalNum))
	log.SyslogInfo("peerIntersaction", "peerCount", peerCount)
	peerIntersaction.SetWaiting(int(peerCount + 1))

	// 5
	peerGroupStep := step.CreateMpcPeerGroupStep(int(totalNum), signNum)
	log.SyslogInfo("peerGroupStep", "peerCount", peerCount)
	peerGroupStep.SetWaiting(int(peerCount + 1))

	// 6
	lagStepPreValueKeys := mpcprotocol.GetPreSetKeyArr(mpcprotocol.MpcSignARSeed, signNum)
	lagStepResultKeys := mpcprotocol.GetPreSetKeyArr(mpcprotocol.MpcSignARResult, signNum)
	ARLag := step.CreateTXSign_Lagrange_Step(int(totalNum), lagStepPreValueKeys, lagStepResultKeys)
	ARLag.SetWaiting(int(peerCount + 1))

	// 7
	TXSignLag := step.CreateTxSign_CalSignStep(int(totalNum), mpcprotocol.MpcTxSignResult, signNum)
	TXSignLag.SetWaiting(int(peerCount + 1))

	mpc.setMpcStep(firstStep, readyStep, JRJZ, AGPoint, peerIntersaction, peerGroupStep, ARLag, TXSignLag)

	for index, step := range mpc.MpcSteps {
		step.SetStepId(index)
		step.SetWaitAll(false)
		step.SetMpcContext(mpc)
		step.SetMpcContextId(mpc.ContextID)
	}

	for _, step := range mpc.MpcSteps {
		msgGens := step.GetMsgGens()
		if msgGens != nil {
			for _, msgGen := range msgGens {
				msgGen.SetContextId(mpc.ContextID)
			}
		}
	}

	return mpc, nil
}

func getSignNumFromTxInfo(mpc *MpcContext) (int, error) {
	signNum := 1
	chainType, err := mpc.mpcResult.GetByteValue(mpcprotocol.MpcChainType)
	if err != nil {
		log.SyslogErr("getSignNumFromTxInfo, get chainType fail", "err", err.Error())
		return 0, err
	}

	if string(chainType) == "BTC" {
		btcTxData, err := mpc.mpcResult.GetByteValue(mpcprotocol.MpcTransaction)
		if err != nil {
			log.SyslogErr("getSignNumFromTxInfo, get tx rlp date fail", "err", err.Error())
			return 0, err
		}

		var args btc.MsgTxArgs
		err = rlp.DecodeBytes(btcTxData, &args)
		if err != nil {
			log.SyslogErr("getSignNumFromTxInfo, decode tx rlp data fail", "err", err.Error())
			return 0, err
		}

		signNum = len(args.TxIn)
	}

	log.SyslogInfo("getSignNumFromTxInfo, succeed", "signNum", signNum)
	return signNum, nil
}

/// add for BTC end
